#include "PluginManager.h"
#include <iostream>

PluginManager* PluginManager::_pm = 0 ;

PluginManager::PluginManager() :
	_destroySensor(0), _destroyActuator(0),
	_robot(0), _map(0)
{
	
}

PluginManager::~PluginManager()
{
	for(size_t i = 0 ; i < _libraries.size() ; i++)
	{
		dlclose(_libraries[i]) ;
	}
}

PluginManager& PluginManager::getInstance()
{
	if(_pm == 0)
		_pm = new PluginManager() ;
	return *(_pm) ;
}

void PluginManager::killInstance()
{
	if(_pm != 0)
		delete _pm ;
}

void PluginManager::setRobotMap(RobotMap* robotMap)
{
	_map = robotMap ;
}

void PluginManager::setRobot(Robot* robot)
{
	_robot = robot ;
}

void PluginManager::loadLibrary(std::string fileName)
{
	void* lib = dlopen(fileName.c_str(), RTLD_LAZY) ;
	if(lib == 0)
	{
		std::cerr << "Unable to load " << fileName << std::endl ;
		return ;
	}
	
	_libraries.push_back(lib) ;
	
	char* dlsym_error = 0 ;
	
	pluginsFunction pF = (pluginsFunction) dlsym(lib, "getPluginsDefinition") ;
	dlsym_error = dlerror() ;
	if (dlsym_error) {
		std::cerr << "Unable to load plugins definiton : " << dlsym_error << std::endl ;
		return ;
	}
	
	std::vector<PluginDef> plugins = pF() ;
	for(size_t i = 0 ; i < plugins.size() ; i++)
	{
		if(plugins[i].pluginType == SENSOR)
		{
			_sensors.insert(std::pair<ClassDescription, void*>(std::pair<std::string, std::string>(plugins[i].className, 
																								   plugins[i].classDescription), lib)) ;
		}
		else
		{
			_actuators.insert(std::pair<ClassDescription, void*>(std::pair<std::string, std::string>(plugins[i].className, 
																									 plugins[i].classDescription), lib)) ;
		}
	}
	
	if(_destroySensor == 0)
	{
		_destroySensor = (destroy_sensor_function) dlsym(lib, "destroy_sensor") ;
		char* dlsym_error = dlerror();
		if (dlsym_error) {
			std::cerr << "Unable to find sensors destructor : " << dlsym_error << std::endl ;
			_destroySensor = 0 ;
		}
	}
	
	if(_destroyActuator == 0)
	{
		_destroyActuator = (destroy_actuator_function) dlsym(lib, "destroy_actuator") ;
		char* dlsym_error = dlerror();
		if (dlsym_error) {
			std::cerr << "Unable to find actuators destructor : " << dlsym_error << std::endl ;
			_destroyActuator = 0 ;
		}
	}
}

std::vector<ClassDescription> PluginManager::sensors() const
{
	std::vector<ClassDescription> res ;
	std::map<ClassDescription, void*>::const_iterator it ;
	for(it = _sensors.begin() ; it != _sensors.end() ; it++)
	{
		res.push_back((*it).first);
	}
	return res ;
}

std::vector<ClassDescription> PluginManager::actuators() const
{
	std::vector<ClassDescription> res ;
	std::map<ClassDescription, void*>::const_iterator it ;
	for(it = _actuators.begin() ; it != _actuators.end() ; it++)
	{
		res.push_back((*it).first);
	}
	return res ;
}

Sensor* PluginManager::createSensor(const std::string& sensorClassName) const
{
	std::string constructorName = "create_" + sensorClassName ;
	void* library = sensorLibrary(sensorClassName) ;
	if(library == 0)
	{
		std::cerr << "Unable to find " << sensorClassName << " in any plugin" << std::endl ;
		return 0 ;
	}
	
	create_sensor_function create_sensor = (create_sensor_function) dlsym(library, constructorName.c_str()) ;
	char* dlsym_error = 0 ;
	dlsym_error  = dlerror();
	if (dlsym_error) {
		std::cerr << "Unable to load sensor " <<  sensorClassName << " : " << dlsym_error << std::endl ;
		return 0 ;
	}
	
	Sensor* res = create_sensor(_map, _robot) ;
	return res ;
}

Actuator* PluginManager::createActuator(const std::string& actuatorClassName) const 
{
	std::string constructorName = "create_" + actuatorClassName ;
	void* library = actuatorLibrary(actuatorClassName) ;
	if(library == 0)
	{
		std::cerr << "Unable to find " << actuatorClassName << " in any plugin" << std::endl ;
		return 0 ;
	}
	
	create_actuator_function create_actuator = (create_actuator_function) dlsym(library, constructorName.c_str()) ;
	char* dlsym_error = 0 ;
	dlsym_error  = dlerror();
	if (dlsym_error) {
		std::cerr << "Unable to load actuator " <<  actuatorClassName << " : " << dlsym_error << std::endl ;
		return 0 ;
	}
	
	Actuator* res = create_actuator(_map, _robot) ;
	return res ;
}

void* PluginManager::sensorLibrary(const std::string& sensorClassName) const
{
	std::map<ClassDescription, void*>::const_iterator it ;
	for(it = _sensors.begin() ; it != _sensors.end() ; it++)
	{
		if(it->first.first == sensorClassName)
			return it->second ;
	}
	return 0 ;
}

void* PluginManager::actuatorLibrary(const std::string& actuatorClassName) const
{
	std::map<ClassDescription, void*>::const_iterator it ;
	for(it = _actuators.begin() ; it != _actuators.end() ; it++)
	{
		if(it->first.first == actuatorClassName)
			return it->second ;
	}
	return 0 ;
}


void PluginManager::destroySensor(Sensor* sensor) const
{
	if(sensor)
	{
		if(_destroySensor)
			_destroySensor(sensor) ;
		else
			std::cerr << "Unable to find sensors destructor" << std::endl ;
	}
}

void PluginManager::destroyActuator(Actuator* actuator) const 
{
	if(actuator)
	{
		if(_destroyActuator)
			_destroyActuator(actuator) ;
		else
			std::cerr << "Unable to find actuators destructor" << std::endl ;
	}
}
